#include "GameManager.hh"
#include "Item.hh"
#include <iostream>
#include <limits>
#include <algorithm>
#include <random>
#include <set>
#include <ctime>

namespace DungeonRPG {

GameManager::GameManager() {
    // Initialize player, map, and seed RNG
    player = new Player("Crusader", 100, 15, 5);
    currentRoom = nullptr;
    map = new DungeonMap();
    std::srand(static_cast<unsigned>(std::time(nullptr)));
}

GameManager::~GameManager() {
    // Clean up dynamically allocated memory
    delete player;
    delete map;
    for (auto room : rooms) delete room;
}

void GameManager::displayMainMenu() {
    // Print list of all player commands
    std::cout << "\n=== COMMAND MENU ===\n";
    std::cout << "Available commands:\n";
    std::cout << " - move      (move to another room)\n";
    std::cout << " - take      (collect items in the room)\n";
    std::cout << " - attack    (fight enemies in the room)\n";
    std::cout << " - use       (use an item)\n";
    std::cout << " - inventory (view your items)\n";
    std::cout << " - equip     (equip an accessory)\n";
    std::cout << " - unequip   (remove equipped accessory)\n";
    std::cout << " - stats     (view your character stats)\n";
    std::cout << " - map       (view dungeon map)\n";
    if (currentRoom && currentRoom->getType() == RoomType::Shop)
        std::cout << " - buy       (shop command, if applicable)\n";
    if (currentRoom && currentRoom->getType() == RoomType::Treasure)
        std::cout << " - choose    (select a reward in treasure room)\n";
    std::cout << " - exit      (quit the game)\n";
    std::cout << "> ";
}

void GameManager::clearScreen() const {
    // Platform-specific screen clearing
#ifdef _WIN32
    system("cls");
#else
    system("clear");
#endif
}

void GameManager::handlePlayerCommand() {
    // Read and normalize player input
    std::string input;
    std::getline(std::cin, input);
    std::transform(input.begin(), input.end(), input.begin(), ::tolower);

    clearScreen();

    if (input == "move") {
        // Prevent moving if enemies are present
        if (!currentRoom->getEnemies().empty()) {
            std::cout << "You cannot leave until all enemies are defeated!\n";
        } else {
            // Handle movement and update position
            std::string dir;
            std::cout << "Direction (north/south/east/west): ";
            std::getline(std::cin, dir);
            Room* next = currentRoom->getConnectedRoom(dir);
            if (next) {
                if (dir == "north") playerY++;
                else if (dir == "south") playerY--;
                else if (dir == "east")  playerX++;
                else if (dir == "west")  playerX--;
                currentRoom = next;
                map->setPlayerPosition(playerX, playerY);
                consumeTorch();
            } else {
                std::cout << "Can't move in that direction.\n";
            }
        }
    } else if (input == "take") {
        // Pickup items if room is cleared
        if (currentRoom->getEnemies().empty()) {
            auto& items = currentRoom->getItems();
            for (auto item : items) {
                std::cout << "Picked up: " << item->getName() << "\n";
                player->addToInventory(item);
            }
            items.clear();
        } else {
            std::cout << "You must defeat enemies first!\n";
        }
    } else if (input == "attack") {
        // Battle all alive enemies in the room
        auto& enemies = currentRoom->getEnemies();
        for (auto enemy : enemies) {
            if (enemy->isAlive()) {
                enterBattle(enemy);
                consumeTorch();
            }
        }
        currentRoom->removeDeadEnemies();
    } else if (input == "inventory") {
        // Show inventory
        player->displayInventory();
    } else if (input == "stats") {
        // Show stats and torch status
        player->display();
        displayTorchStatus();
    } else if (input == "map") {
        // Display map with player position
        map->displayMap(playerX, playerY);
    } else if (input == "use") {
        // Use item by name
        if (player->getInventory().empty()) {
            std::cout << "You have no items to use.\n";
        } else {
            std::cout << "Available items:\n";
            player->displayInventory();
            std::cout << "Enter item name: ";
            std::string name;
            std::getline(std::cin, name);
            if (name == "Torch") adjustTorch(3); // Torch has special behavior
            player->useItem(name);
        }
    } else if (input == "equip") {
        // Equip item from inventory
        player->displayInventory();
        std::cout << "Enter item name to equip: ";
        std::string name;
        std::getline(std::cin, name);
        player->equipFromInventory(name);
    } else if (input == "unequip") {
        // Remove accessory from specific slot
        player->showAccessories();
        std::cout << "Enter slot number (1-4) to unequip: ";
        int index;
        std::cin >> index;
        std::cin.ignore();
        player->unequipAccessory(index - 1);
    } else if (input == "buy") {
        // Shop command validation
        if (currentRoom->getType() == RoomType::Shop) enterShop();
        else std::cout << "You are not in a shop.\n";
    } else if (input == "choose") {
        // Treasure room reward selection
        if (currentRoom->getType() == RoomType::Treasure) enterTreasureRoom();
        else std::cout << "You are not in a treasure room.\n";
    } else if (input == "exit") {
        // End game
        std::cout << "You chose to abandon the expedition. Goodbye.\n";
        exit(0);
    } else {
        // Invalid command
        std::cout << "Unknown command.\n";
    }

    displayMainMenu();
}

void GameManager::adjustTorch(int delta) {
    // Clamp torchlight to range 0-12
    torchlight = std::clamp(torchlight + delta, 0, 12);
}

void GameManager::displayTorchStatus() const {
    // Show current torchlight level and its effect
    std::cout << "Torchlight: [" << torchlight << "/12] ";
    if (torchlight >= 9)
        std::cout << "- Bright (Bonus loot chance +15%)\n";
    else if (torchlight >= 5)
        std::cout << "- Normal\n";
    else
        std::cout << "- Dim (Enemies more aggressive, stress +10%)\n";
}

void GameManager::consumeTorch() {
    // Reduce torchlight by 1 and show status
    adjustTorch(-1);
    displayTorchStatus();
}

bool GameManager::isVictoryConditionMet() const {
    // Check if chosen victory condition is fulfilled
    if (victoryMode == "explore") return exploredCount >= rooms.size() * 0.9;
    else return allEnemiesDefeated();
}

void GameManager::gameLoop() {
    setup();

    // Ask player for victory condition
    std::string modeInput;
    while (true) {
        std::cout << "Choose victory condition:\n1. Explore 90% of rooms\n2. Defeat all enemies\n> ";
        std::getline(std::cin, modeInput);
        if (modeInput == "1") {
            victoryMode = "explore";
            break;
        } else if (modeInput == "2") {
            victoryMode = "combat";
            break;
        } else {
            std::cout << "Invalid input. Please enter 1 or 2.\n";
        }
    }

    // Main gameplay loop until win or death
    while (player->isAlive() && !isVictoryConditionMet()) {
        if (!currentRoom->isExplored()) {
            currentRoom->markExplored();
            exploredCount++;
        }
        currentRoom->describe();
        displayMainMenu();
        handlePlayerCommand();
    }

    // Prompt player to optionally keep playing after victory
    std::string ans;
    while (true) {
        std::cout << "\nVictory condition met! Do you want to continue exploring? (yes/no): ";
        std::getline(std::cin, ans);
        std::transform(ans.begin(), ans.end(), ans.begin(), ::tolower);
        if (ans == "no") {
            std::cout << "\nYou ended your journey with honor.\n";
            return;
        } else if (ans == "yes") {
            break;
        } else {
            std::cout << "Invalid input. Please enter 'yes' or 'no'.\n";
        }
    }

    // Continue exploring until death
    while (player->isAlive()) {
        if (!currentRoom->isExplored()) {
            currentRoom->markExplored();
            exploredCount++;
        }
        currentRoom->describe();
        displayMainMenu();
        handlePlayerCommand();
    }

    std::cout << "\nYour journey ends here...\n";
}

bool GameManager::allEnemiesDefeated() const {
    // Return true only if all rooms have no enemies
    for (const auto& room : rooms)
        if (!room->getEnemies().empty()) return false;
    return true;
}

void GameManager::enterBattle(Enemy* enemy) {
    // Begin battle loop
    std::cout << "\nBattle Start: " << player->getName() << " vs " << enemy->getName() << "\n";
    while (enemy->isAlive() && player->isAlive()) {
        player->display();
        player->reduceSkillCooldowns();

        std::cout << "Enemy HP: " << enemy->getHealth() << "\n";
        std::cout << "1. Attack\n2. Use Item\n3. Use Skill\nChoose action: ";

        std::string input;
        std::getline(std::cin, input);
        bool playerActed = false;

        if (input == "1") {
            // Normal attack
            player->performAttack(*enemy);
            playerActed = true;
        } else if (input == "2") {
            // Use item from inventory
            std::cout << "Available items:\n";
            player->displayInventory();
            std::string itemName;
            std::cout << "Enter item name to use: ";
            std::getline(std::cin, itemName);
            if (player->useItem(itemName)) {
                playerActed = true;
            } else {
                std::cout << "No item used.\n";
            }
        } else if (input == "3") {
            // Use skill by slot
            player->showEquippedSkills();
            std::cout << "Enter skill slot (1-4): ";
            std::string slotStr;
            std::getline(std::cin, slotStr);
            try {
                int idx = std::stoi(slotStr);
                Skill* skill = player->getSkill(idx - 1);
                if (skill && skill->isReady()) {
                    if (dynamic_cast<HealWounds*>(skill)) {
                        skill->activate(player, player);  // Self heal
                    } else {
                        skill->activate(player, enemy);  // Attack enemy
                    }
                    playerActed = true;
                } else {
                    std::cout << "Invalid or not ready skill.\n";
                }
            } catch (...) {
                std::cout << "Invalid input.\n";
            }
        } else {
            std::cout << "Invalid choice.\n";
        }

        // Enemy takes turn if alive and player acted
        if (playerActed && enemy->isAlive()) {
            if (enemy->isStunned()) {
                std::cout << enemy->getName() << " is stunned and skips its turn!\n";
                enemy->recoverStun(); // Recover from stun
            } else {
                enemy->performAttack(*player);
            }
        }
    }

    // Handle loot and drops if enemy dies
    if (!enemy->isAlive()) {
        player->Character::resetBattleBuffs();
        enemy->resetBattleBuffs();
        std::cout << enemy->getName() << " has been defeated!\n";

        int goldDrop = std::rand() % 16 + 10;
        std::cout << "You loot " << goldDrop << " gold!\n";
        player->addGold(goldDrop);

        player->tryLearnSkill();

        int dropRoll = std::rand() % 100;
        if (dropRoll < 40) {
            std::vector<Item*> possibleDrops = {
                new Item("Food", "Restores 5 HP.", 5, 0, 0, ItemType::Food),
                new Item("Bandage", "Reduces stress slightly.", 0, 0, 0, ItemType::Bandage),
                &LuckyCharm,
                &RustyRing
            };
            Item* dropItem = possibleDrops[std::rand() % possibleDrops.size()];
            std::cout << "You found: " << dropItem->getName() << " - " << dropItem->getDescription() << "\n";
            currentRoom->addItem(dropItem);
        }

        int scrollChance = rand() % 100;
        if (scrollChance < 1) {
            std::vector<Skill*> scrollSkills = {
                new Fireball(), new MarkTarget(), new Fortify(),
                new LightningStrike(), new RallyCry(), new DarkPact(),
            };

            // 随机选择一个作为掉落
            int dropIndex = rand() % scrollSkills.size();
            Skill* dropSkill = scrollSkills[dropIndex];

            // 创建卷轴并绑定技能
            Item* scroll = new Item(
                dropSkill->name + " Scroll",
                "Learn skill: " + dropSkill->description,
                0, 0, 0, ItemType::Scroll
            );
            scroll->specialSkill = dropSkill;
            currentRoom->addItem(scroll);
            std::cout << "A scroll dropped: " << scroll->name << "\n";

            // ✅ 释放未被使用的其他技能
            for (size_t i = 0; i < scrollSkills.size(); ++i) {
                if (i != dropIndex) delete scrollSkills[i];
            }
        }

    }
}

void GameManager::setup() {
    // Random generators for room count, features, and enemy/item types
    std::mt19937 rng(static_cast<unsigned>(time(nullptr)));
    std::uniform_int_distribution<int> roomCountDist(8, 14);
    std::uniform_real_distribution<float> shopChance(0.0f, 1.0f);
    std::uniform_real_distribution<float> treasureChance(0.0f, 1.0f);
    std::uniform_int_distribution<int> enemyTypeDist(0, 3);

    int roomCount = roomCountDist(rng);
    std::map<std::pair<int,int>, Room*> coordMap;
    std::vector<std::pair<int,int>> path;

    // Start at origin
    int x = 0, y = 0;
    coordMap[{x,y}] = new Room("Room","A mysterious chamber.");
    path.push_back({x,y});

    // Random walk to generate room path with unique coordinates
    for (int i=1; i<roomCount; ++i) {
        int dx = 0, dy = 0;
        switch(std::uniform_int_distribution<int>(0,3)(rng)) {
            case 0: dy = 1; break;
            case 1: dy = -1; break;
            case 2: dx = 1; break;
            case 3: dx = -1; break;
        }
        int nx = x + dx, ny = y + dy;
        while (coordMap.count({nx,ny})) {
            dx = dy = 0;
            switch(std::uniform_int_distribution<int>(0,3)(rng)) {
                case 0: dy = 1; break;
                case 1: dy = -1; break;
                case 2: dx = 1; break;
                case 3: dx = -1; break;
            }
            nx = x + dx;
            ny = y + dy;
        }
        x = nx;
        y = ny;
        coordMap[{x,y}] = new Room("Room","A mysterious chamber.");
        path.push_back({x,y});
    }

    // Fill roomMap and master room list
    for (auto& kv : coordMap) {
        roomMap[kv.first] = kv.second;
        rooms.push_back(kv.second);
    }

    // Bi-directional connection of adjacent rooms
    auto link = [&](Room* a, const std::string& da, Room* b, const std::string& db) {
        a->addConnection(da, b);
        b->addConnection(db, a);
    };
    for (auto& kv : roomMap) {
        auto [cx, cy] = kv.first;
        Room* r = kv.second;
        if (roomMap.count({cx, cy+1})) link(r, "north", roomMap[{cx, cy+1}], "south");
        if (roomMap.count({cx, cy-1})) link(r, "south", roomMap[{cx, cy-1}], "north");
        if (roomMap.count({cx+1, cy})) link(r, "east",  roomMap[{cx+1, cy}], "west");
        if (roomMap.count({cx-1, cy})) link(r, "west",  roomMap[{cx-1, cy}], "east");
    }

    // Set starting room
    auto [sx, sy] = path.front();
    playerX = sx; playerY = sy;
    currentRoom = roomMap[{sx,sy}];
    map->setPlayerPosition(sx, sy);
    map->setRoomMap(roomMap);

    // Randomized room contents: shop, treasure, or enemies/items
    std::uniform_int_distribution<int> e(0, 3), i(0, 1), cnt(1, 2);
    for (auto room : rooms) {
        float r = shopChance(rng), g = treasureChance(rng);

        if (r < 0.15) {
            // Shop room setup
            room->setType(RoomType::Shop);

            std::vector<Item*> consumables = {
                new Item("Food",      "Restores health.",                         5, 0, 0, ItemType::Food),
                new Item("Bandage",   "Stops bleeding and reduces stress.",       0, 0, 0, ItemType::Bandage),
                new Item("Holy Water","Reduces stress and protects against curse.",0, 0, 0, ItemType::HolyWater),
                new Item("Torch",     "Light up the path.",                       0, 0, 0, ItemType::Torch)
            };
            std::shuffle(consumables.begin(), consumables.end(), rng);

            std::vector<Item*> accessories = {
                LuckyCharm.clone(), RustyRing.clone(), SharpTrinket.clone(),
                HeavyPendant.clone(), VirtueBadge.clone(), BloodCharm.clone(), OccultNecklace.clone()
            };
            std::shuffle(accessories.begin(), accessories.end(), rng);

            std::vector<Item*> stock = {
                consumables[0], consumables[1],
                accessories[0], accessories[1]
            };
            room->setShopStock(stock);

        } else if (g < 0.1f) {
            // Treasure room setup
            room->setType(RoomType::Treasure);

        } else {
            // Enemy room setup
            if (e(rng) <= 2) {
                int c = cnt(rng);
                for (int j = 0; j < c; ++j) {
                    switch (enemyTypeDist(rng)) {
                        case 0: room->addEnemy(new Enemy("Cultist", 30, 6, 2)); break;
                        case 1: room->addEnemy(new BruteEnemy()); break;
                        case 2: room->addEnemy(new ArbalestEnemy()); break;
                        case 3: room->addEnemy(new CultistMage()); break;
                    }
                }
            }
            // Randomly place a torch
            if (i(rng)) room->addItem(new Item("Torch", "Boost light.", 0, 0, 0, ItemType::Torch));
        }
    }
}

void GameManager::enterShop() {
    auto& stock = currentRoom->getShopStock();

    // Handle empty shop
    if (stock.empty()) {
        std::cout << "The shop has nothing left to sell.\n";
        return;
    }

    // Display shop inventory
    std::cout << "\n=== SHOP INVENTORY ===\n";
    for (size_t idx = 0; idx < stock.size(); ++idx) {
        std::cout << idx + 1 << ". " << stock[idx]->getName()
                  << " - " << stock[idx]->getDescription() << " (Cost:20 gold)\n";
    }

    // Handle user input for purchase
    std::cout << "Enter item number to buy or 0 to cancel: ";
    int choice;
    std::cin >> choice;
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    if (choice <= 0 || choice > (int)stock.size()) return;

    if (player->spendGold(20)) {
        Item* purchased = stock[choice - 1]->clone();
        player->addToInventory(purchased);
        std::cout << "Item purchased and added to your inventory.\n";
        currentRoom->removeShopItem(choice - 1);
    } else {
        std::cout << "You don't have enough gold!\n";
    }
}

void GameManager::enterTreasureRoom() {
    // Prevent claiming reward more than once
    if (currentRoom->isCleared()) {
        std::cout << "You already claimed the treasure.\n";
        return;
    }

    // Present reward options
    std::vector<Item*> choices = { &VirtueBadge, &BloodCharm, &OccultNecklace };
    std::cout << "\n=== TREASURE ROOM ===\nChoose one item:\n";
    for (size_t i = 0; i < choices.size(); ++i)
        std::cout << i + 1 << ". " << choices[i]->getName() << " - " << choices[i]->getDescription() << "\n";

    std::cout << "Enter your choice (1-3): ";
    int sel;
    std::cin >> sel;
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

    if (sel >= 1 && sel <= (int)choices.size()) {
        player->addToInventory(choices[sel - 1]);
        std::cout << "You selected: " << choices[sel - 1]->getName() << "\n";
        currentRoom->markCleared();
    }
}
}